﻿// C:\Users\Jorge\Documents\Visual Studio 2010\Projects\C#
//   \LIB\Util_Lib\UtilFn.cs
//
// Implementation of common utility functions.
//
// Programmer:  Jorge L. Orejel
//
// Last update: 02/22/2022 : Code to deal with zero-comparisons of {double}s.
//
//      03/01/2021 : Cosmetic changes in documentation comments.
//
//      11/12/2020 : Enumeration {CmpResult} and all the print, comparison,
//                      and string-related functions moved to {PrnCmpStrLib}.
//
//      11/11/2020 : Additional comments.
//
//      07/21/2020 : Function {FloorLog2}.
////
//      06/03/2020 : Function {IsDivisible}.
//
//      06/02/2020 : Functions {IsPowerOf2} and {CountOfBitsSet}.

using System;

namespace Util_Lib
{
   public static class UtilFn
   {
      // Definitions to deal with zero-comparisons of {double}s.
      //
      // After Microsoft.Docs "Double.Equals Method".
      // https://docs.microsoft.com/en-us/dotnet/api/system.double.equals?view=net-5.0

      private static double fraction = (double)0.333333,
                            dTolerance = Math.Abs( fraction * (double)0.00001 ),
                            zero = (double)0.0;
      //
      // In comparisons, use Math.Abs( {double}1 - {double}2 ) <= {dTolerance} )
      // (see function {EQdoubles}).

      /// <summary>Determine whether two {double} numbers are "equal". 
      /// </summary>
      /// <param name="d1">Double number.</param>
      /// <param name="d2">Double number.
      /// </param>
      /// <returns>Whether {d1} "equals" {d2}.
      /// </returns>
      public static bool EQdoubles( double d1, double d2 )
      {
         d1 = Math.Abs( d1 );
         d2 = Math.Abs( d2 );

         return Math.Abs( d1 - d2 ) <= dTolerance;
      }// EQdoubles

      /// <summary>Determine whether a {double} is close to {zero}. 
      /// </summary>
      /// <param name="d">{double} to be tested.
      /// </param>
      /// <returns>Whether {d} is close to {zero}.
      /// </returns>
      public static bool NearZero( double d )
      {
         return EQdoubles( d, zero );
      }// NearZero

      /// <summary>Determine whether a double is divisible by another double.
      /// </summary>
      /// <param name="x">Numerator of division.
      /// </param>
      /// <param name="y">Denominator of division.
      /// </param>
      /// <returns>Whether {x} is divisible by {y}.
      /// </returns>
      public static bool IsDivisible( double x, double y )
      {
         return Math.Abs( ( ( Math.Round( x / y ) * y ) - x ) ) <= ( 1.0E-9 * y );
      }// IsDivisible

      /// <summary>Determine whether a positive integer is a power of 2.
      /// 
      ///          All positive integers that are powers of 2 have only one bit set.
      /// </summary>
      /// <param name="i">Integer argument.
      /// </param>
      /// <returns>Whether or not the argument is a power of 2.
      /// </returns>
      public static bool IsPowerOf2( int i )
      {
         return i > 0 && CountOfBitsSet( i ) == 1;
      }// IsPowerOf2

      /// <summary>Determine the number of bits set in a given integer.
      /// </summary>
      /// <param name="i">Integer argument.
      /// </param>
      /// <returns>The number of bits set in the argument.
      /// </returns>
      public static int CountOfBitsSet( int i )
      {
         int count = 0;

         while ( i > 0 )
         {
            count += i & 1;
            i >>= 1;
         }
         return count;
      }// CountOfBitsSet

      /// <summary>Compute the floor of the base-2 logarithm of an integer.
      /// </summary>
      /// <param name="i">Integer argument.
      /// </param>
      /// <returns>The floor of log2( i ).
      /// </returns>
      public static int FloorLog2( int i )
      {
         int k = 0;
         int pOf2 = 1;

         while ( pOf2 < i )
         {
            ++k;
            pOf2 <<= 1;
         }
         if ( pOf2 > i )
         {
            --k;
         }
         return k;
      }// FloorLog2
   }// UtilFn (static class)
}// Util_Lib (namespace)
